package cn.sskxyz.mongodb.compress;

import java.io.InputStream;
import java.util.Base64;
import java.util.List;

import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.MongoNamespace;

import cn.sskxyz.mongodb.zlib.ZlibCodec;

public class CompressProcessor {

	private ZlibCodec codec = new ZlibCodec();

	private ObjectMapper mapper = new ObjectMapper();

	private CompressKeyMatcher matcher = new DefaultCompressKeyMatcher();

	private static final Logger logger = LoggerFactory.getLogger(CompressProcessor.class);
	
	public void setMatcher(CompressKeyMatcher matcher) {
		this.matcher = matcher;
	}

	public void decompress(String key, Document doc, CompressKey ckey) {
		int idx = key.indexOf('.');
		if (idx > 0) {
			String prefix = key.substring(0, idx);
			Object innerDoc = doc.get(prefix);
			if (innerDoc instanceof Document) {
				decompress(key.substring(idx + 1), (Document) innerDoc, ckey);
			} else {
				return;
			}
		}
		doc.computeIfPresent(key, (k, v) -> {
			try {
				if (v instanceof Document) {
					return v;
				}
				byte[] data = Base64.getDecoder().decode(String.valueOf(v));
				InputStream is = codec.decompressToStream(data);
				return mapper.readValue(is, ckey.getActualType());
			} catch (Exception e) {
				logger.error("解压缩数据异常", e);
			}
			return v;
		});
	}

	public void decompress(Object result, List<CompressKey> compressKeys, MongoNamespace namespace) {
		if (result == null) {
			return;
		}
		if (compressKeys == null || compressKeys.size() == 0) {
			return;
		}
		if (result instanceof Document) {
			Document doc = (Document) result;
			compressKeys.stream().filter(k -> matcher.match(k, namespace)).forEach(ck -> {
				decompress(ck.getKey(), doc, ck);
			});
		}
	}

	public void compressValue(String key, Document document) {
		int idx = key.indexOf('.');
		if (idx > 0 && !document.containsKey(key)) {
			String prefix = key.substring(0, idx);
			Object innerDoc = document.get(prefix);
			if (innerDoc instanceof Document) {
				compressValue(key.substring(idx + 1), (Document) innerDoc);
			} else {
				return;
			}
		}

		document.computeIfPresent(key, (k, v) -> {
			try {
				return codec.compress(mapper.writeValueAsBytes(v));
			} catch (Exception e) {
				logger.error("压缩数据时异常", e);
			}
			return v;
		});
	}

	public void compressValue(Document document, List<CompressKey> compressKeys, MongoNamespace namespace) {
		if (document == null) {
			return;
		}

		if (compressKeys == null || compressKeys.size() == 0) {
			return;
		}
		final Document rootDoc = document.containsKey("$set") ? (Document) document.get("$set") : document;
		compressKeys.stream().filter(ck -> matcher.match(ck, namespace)).forEach(cmk -> {
			String updateKey = cmk.getKey();
			compressValue(updateKey, rootDoc);
		});
	}
}
